package weixin

import (
	"net/http"
	"io/ioutil"
	"github.com/astaxie/beego/logs"
	"encoding/json"
	"fmt"
	"strings"
	"sort"
	"crypto/sha1"
	"encoding/hex"
)

const (
	WEIXIN           = "____WEIXIN____"
	APPIDS           = "APPID"
	APPSECRETS       = "APPSECRET"
	ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=%s&secret=%s"
)

//微信开发API, 官方文档: https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1445241432
type WeiXin struct {
	Url            string  `json:",omitempty"`
	Name           string
	Token          string //令牌
	Appid          string //开发者ID是公众号开发识别码，配合开发者密码可调用公众号的接口能力。
	Appsecret      string //开发者密码是校验公众号开发者身份的密码，具有极高的安全性。切记勿把密码直接交给第三方开发者或直接存储在代码中。如需第三方代开发公众号，请使用授权方式接入。
	EncodingAESKey string //消息加解密密钥
	EncodingType   string //消息加解密方式
	AccessToken    AccessToken
	Menu           Menu      //菜单
	Handlers       []Handler //处理微信消息
}

type AccessToken struct {
	AccessToken string  `json:"access_token"`
	ExpiresIn   float64 `json:"expires_in"`
}

func NewWeiXin(name, token, appid, appsecret string) (wx WeiXin) {
	wx.Name = name
	wx.Token = token
	wx.Appid = appid
	wx.Appsecret = appsecret
	wx.AccessToken = AccessToken{}
	return
}

func (this *WeiXin) CheckSignature(signature, timestamp, noce string) bool {
	tobe := []string{this.Token, timestamp, noce}
	sort.Strings(tobe)
	result := strings.Join(tobe, "")
	h := sha1.New()
	h.Write([]byte(result)) // 需要加密的字符串为 123456
	cipherStr := h.Sum(nil)
	if signature == hex.EncodeToString(cipherStr) {
		return true
	}
	return false
}

// 获取access_token
// access_token是公众号的全局唯一接口调用凭据，公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时，需定时刷新，重复获取将导致上次获取的access_token失效。
//
// 公众平台的API调用所需的access_token的使用及生成方式说明：
// 1、建议公众号开发者使用中控服务器统一获取和刷新Access_token，其他业务逻辑服务器所使用的access_token均来自于该中控服务器，不应该各自去刷新，否则容易造成冲突，导致access_token覆盖而影响业务；
// 2、目前Access_token的有效期通过返回的expire_in来传达，目前是7200秒之内的值。中控服务器需要根据这个有效时间提前去刷新新access_token。在刷新过程中，中控服务器对外输出的依然是老access_token，此时公众平台后台会保证在刷新短时间内，新老access_token都可用，这保证了第三方业务的平滑过渡；
// 3、Access_token的有效时间可能会在未来有调整，所以中控服务器不仅需要内部定时主动刷新，还需要提供被动刷新access_token的接口，这样便于业务服务器在API调用获知access_token已超时的情况下，可以触发access_token的刷新流程。
//
// 公众号可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在“微信公众平台-开发-基本配置”页中获得（需要已经成为开发者，且帐号没有异常状态）。调用接口时，请登录“微信公众平台-开发-基本配置”提前将服务器IP地址添加到IP白名单中，点击查看设置方法，否则将无法调用成功。
//
// 接口调用请求说明
//
// https请求方式: GET
// https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
//
// 参数说明
//
//
// 参数 	是否必须 	说明
// grant_type	是	获取access_token填写client_credential
// appid	是	第三方用户唯一凭证
// secret	是	第三方用户唯一凭证密钥，即appsecret
// 返回说明
// 正常情况下，微信会返回下述JSON数据包给公众号：
// {"access_token":"ACCESS_TOKEN","expires_in":7200}
//
// 参数说明
//
// 参数	说明
// access_token	获取到的凭证
// expires_in	凭证有效时间，单位：秒
//
//
// 错误时微信会返回错误码等信息，JSON数据包示例如下（该示例为AppID无效错误）:
//
// {"errcode":40013,"errmsg":"invalid appid"}
//
//
// 返回码说明
// 返回码	说明
// -1	系统繁忙，此时请开发者稍候再试
// 0	请求成功
// 40001	AppSecret错误或者AppSecret不属于这个公众号，请开发者确认AppSecret的正确性
// 40002	请确保grant_type字段值为client_credential
// 40164
// 调用接口的IP地址不在白名单中，请在接口IP白名单中进行设置
func (this *WeiXin) RefreshAccessToken() (error *WeiXinError) {
	access_token_url := fmt.Sprintf(ACCESS_TOKEN_URL, this.Appid, this.Appsecret)
	logs.Debug("[%s]刷新token:  请求:%s", this.Name, access_token_url)

	resp, err := http.Get(access_token_url)
	if err != nil || resp.StatusCode != http.StatusOK {
		return NewWeiXinError()
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return NewWeiXinError()
	}
	logs.Debug("微信Token返回信息:%s", body)
	err = json.Unmarshal(body, &this.AccessToken)
	if err != nil {
		return NewWeiXinError()
	}
	return nil
}
func (this *WeiXin) CreateMenuByJson(menus string) *WeiXinError {
	url := fmt.Sprintf(MENU_CREATE_URL, this.AccessToken.AccessToken)
	logs.Info("创建微信菜单:%s", url)
	resp, err := http.Post(url, "appliciation/json", strings.NewReader(menus))
	if err != nil || resp.StatusCode != http.StatusOK {
		return NewWeiXinError()
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return NewWeiXinError()
	}
	logs.Debug("微信返回菜单信息:%s", body)
	return nil
}

//  使用接口创建自定义菜单后，开发者还可使用接口查询自定义菜单的结构。另外请注意，在设置了个性化菜单后，使用本自定义菜单查询接口可以获取默认菜单和全部个性化菜单信息。
//  请求说明
//  http请求方式：GET
//  https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN
//  返回说明（无个性化菜单时）
//  对应创建接口，正确的Json返回结果:
//  {
//  "menu": {
//  "button": [
//  {
//  "type": "click",
//  "name": "今日歌曲",
//  "key": "V1001_TODAY_MUSIC",
//  "sub_button": [ ]
//  },
//  {
//  "type": "click",
//  "name": "歌手简介",
//  "key": "V1001_TODAY_SINGER",
//  "sub_button": [ ]
//  },
//  {
//  "name": "菜单",
//  "sub_button": [
//  {
//  "type": "view",
//  "name": "搜索",
//  "url": "http://www.soso.com/",
//  "sub_button": [ ]
//  },
//  {
//  "type": "view",
//  "name": "视频",
//  "url": "http://v.qq.com/",
//  "sub_button": [ ]
//  },
//  {
//  "type": "click",
//  "name": "赞一下我们",
//  "key": "V1001_GOOD",
//  "sub_button": [ ]
//  }
//  ]
//  }
//  ]
//  }
//  }
//  返回说明（有个性化菜单时）
//  {
//  "menu": {
//  "button": [
//  {
//  "type": "click",
//  "name": "今日歌曲",
//  "key": "V1001_TODAY_MUSIC",
//  "sub_button": [ ]
//  }
//  ],
//  "menuid": 208396938
//  },
//  "conditionalmenu": [
//  {
//  "button": [
//  {
//  "type": "click",
//  "name": "今日歌曲",
//  "key": "V1001_TODAY_MUSIC",
//  "sub_button": [ ]
//  },
//  {
//  "name": "菜单",
//  "sub_button": [
//  {
//  "type": "view",
//  "name": "搜索",
//  "url": "http://www.soso.com/",
//  "sub_button": [ ]
//  },
//  {
//  "type": "view",
//  "name": "视频",
//  "url": "http://v.qq.com/",
//  "sub_button": [ ]
//  },
//  {
//  "type": "click",
//  "name": "赞一下我们",
//  "key": "V1001_GOOD",
//  "sub_button": [ ]
//  }
//  ]
//  }
//  ],
//  "matchrule": {
//  "group_id": 2,
//  "sex": 1,
//  "country": "中国",
//  "province": "广东",
//  "city": "广州",
//  "client_platform_type": 2
//  },
//  "menuid": 208396993
//  }
//  ]
//  }
//  注：menu为默认菜单，conditionalmenu为个性化菜单列表。字段说明请见个性化菜单接口页的说明。
func (this *WeiXin) SearchMenu() *WeiXinError {
	url := fmt.Sprintf(MENU_SEARCH_URL, this.AccessToken.AccessToken)
	logs.Info("查询菜单接口:%s", url)
	resp, err := http.Get(url)
	if err != nil || resp.StatusCode != http.StatusOK {
		return NewWeiXinError()
	}

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return NewWeiXinError()
	}
	logs.Debug("微信返回菜单信息:%s", body)

	err = json.Unmarshal(body, &this.Menu)
	if err != nil {
		return NewWeiXinError()
	}
	return nil
}

func (this *WeiXin) DeleteMenu() {

}

// 添加客服帐号
// http请求方式: POST
// https://api.weixin.qq.com/customservice/kfaccount/add?access_token=ACCESS_TOKEN
// POST数据示例如下：
// {
// "kf_account" : "test1@test",
// "nickname" : "客服1",
// "password" : "pswmd5",
// }
// 返回说明（正确时的JSON返回结果）：
// {
// "errcode" : 0,
// "errmsg" : "ok",
// }
//错误时微信会返回错误码等信息，请根据错误码查询错误信息
func (this *WeiXin) AddKfAccount() {

}

// 修改客服帐号
// 开发者可以通过本接口为公众号修改客服账号。该接口调用请求如下：
// http请求方式: POST
// https://api.weixin.qq.com/customservice/kfaccount/update?access_token=ACCESS_TOKEN
// POST数据示例如下：
// {
// "kf_account" : "test1@test",
// "nickname" : "客服1",
// "password" : "pswmd5",
// }
// 返回说明（正确时的JSON返回结果）：
// {
// "errcode" : 0,
// "errmsg" : "ok",
// }
// 错误时微信会返回错误码等信息，请根据错误码查询错误信息
func (this *WeiXin) UpdateKfAccount() {

}

// 删除客服帐号
// 开发者可以通过该接口为公众号删除客服帐号。该接口调用请求如下：
// http请求方式: GET
// https://api.weixin.qq.com/customservice/kfaccount/del?access_token=ACCESS_TOKEN
// POST数据示例如下：
// {
// "kf_account" : "test1@test",
// "nickname" : "客服1",
// "password" : "pswmd5",
// }
// 返回说明（正确时的JSON返回结果）：
// {
// "errcode" : 0,
// "errmsg" : "ok",
// }
// 错误时微信会返回错误码等信息，请根据错误码查询错误信息
func (this *WeiXin) DeleteKfAccount() {

}

func (this *WeiXin) DoHandler(msg *WeiXinMsg) (replyMsg *WeiXinMsg, canHandler bool) {
	for _, handler := range this.Handlers {
		replyMsg, canHandler = handler.DoHandler(msg)
		if canHandler {
			break
		}
	}
	return
}
